/**
 * WTF Engine
 *
 * License... etc.
 **
 * WTF Network Protocol Implementation
 **
 * Author: Sean Micklethwaite
 **/

#ifndef __net_protocol_implH__
#define __net_protocol_implH__

#include "net_protocol.h"
#include "config.h"
#include "dator.h"

#ifdef __NET_ASIO__
#	include "net_asio.h"
#endif

namespace WtfEngine {

	/**
	 * The all-purpose packet wrapper.
	 **/
	template <class Type = PacketData>
	class Packet : public virtual IPacket, public GcObject {
		/// Keep a reference to the buffer, so it can be shared
		typename NonCopyBuffer<Type>::tRef	mrBuffer;

		public:
			/// Creates a new packet and buffer
			inline Packet()
				: mrBuffer( new NonCopyBuffer<Type>(new Type(), 1) )
			{};

			/// Creates a new packet and buffer
			inline Packet(const Type& data)
				: mrBuffer( new NonCopyBuffer<Type>(new Type(data), 1) )
			{};

			/// Construct from another buffer type (explicitly).
			template <class T>
			inline explicit Packet(const typename NonCopyBuffer<T>::tRef& rBuf)
				: mrBuffer(*reinterpret_cast<typename NonCopyBuffer<Type>::tRef *>(&rBuf))
			{};

			/// We can downcast from derived packet types implicitly
			template <class T>
			inline Packet(const Packet<T>& pkt)
				: mrBuffer(*reinterpret_cast<typename NonCopyBuffer<Type>::tRef *>(&pkt.mrBuffer))
			{
				static_cast<Type *>((T *)1);
			};

			unsigned getSize() const {
				return mrBuffer->getData()->PacketSize;
			};
			const unsigned char * getData() const {
				return (const unsigned char *)(mrBuffer->getData());
			};
			const Type * getPacket() const {
				return mrBuffer->getData();
			};

			GC_AUTOSIZE(Packet);
	};


	/**
	 * Represents a connected peer
	 **/
	class Peer : public GcObject {
		public:
			typedef enum {
				/// Peer has not yet responded to greeting
				PEER_UNIDENTIFIED = 0,

				/// Peer is not a part of the game, but traffic is routed through
				/// for matchmaking, etc.
				PEER_PASSIVE = 1,

				/// Peer is a normal client - this is the default
				PEER_PARTY_GUEST = 2,

				/// Peer has authority over the game; this can only be
				/// assigned by the master server or granted voluntarily
				PEER_PARTY_LEADER = 3,

				/// Peer is the master (stats) server. Connection is initiated
				/// by this node.
				PEER_MASTER_SERVER = 4,

				/// Peer has full access to the engine. This is available to peers
				/// on the local machine and by explicit user request; used by
				/// the editor and debugger.
				PEER_PRIVILEGED = 5
			} tPeerType;

		private:
			/// This TCP socket is used for essential control messages
			TcpSocket::tRef	mrControlSocket;
			/// This socket is used to send data, and can be TCP or UDP.
			ISocket::tRef	mrDataSocket;

			tPeerType		mPeerType;

			std::deque<Packet<>::tRef> mrRecvQueue;

		public:
			explicit Peer(const TcpSocket::tRef& rControlSocket);
			~Peer();

			void SendPacket(const Packet<>::tRef& rPacket);

			// Event handlers
			void OnDataRecv();
	};


	/**
	 * A party is a group of nodes which are arranging/playing a game.
	 **/
	class Party: public GcObject {
	public:
		typedef std::list<Peer::tRef> tPeerList;

		typedef enum {
			/// Nothing happening
			PARTY_IDLE,
			/// Not the owner, not in control
			PARTY_GUEST,
			/// Broadcasting & listening
			PARTY_RECRUITING,
			/// Party locked
			PARTY_IN_TRANSACTION,
			/// Ready to play
			PARTY_READY,
			/// In game
			PARTY_PLAYING,
			/// Finished game, party still intact
			PARTY_FINISHED
		} tPartyStatus;

	private:
		tPeerList   mvPeerList;
		
		unsigned muLocalPartyId;
		unsigned muMinSize, muMaxSize, muGameId;
		int miGameType;

		tPartyStatus mStatus;

		public:
			Party(unsigned gameId, int gameType, unsigned minSize = 2, unsigned maxSize = 8)
			: GcObject(), muMinSize(minSize), muMaxSize(maxSize), muGameId(gameId), miGameType(gameType),
			mStatus(PARTY_IDLE)
			{ };


			/// Tries to recruit more party members by broadcasting over the LAN, on
			/// the specified port.
			/// @pre	Party status is idle.
			void BroadcastOnLan(tPort port);

			PartyBroadcastMessage CreateBroadcastMessage() const;
				
			// Properties
			inline tPartyStatus getStatus() const {
				return mStatus;
			};

			GC_AUTOSIZE(Party);
	};

	class PeerManager: public Singleton<PeerManager> {
		std::list<Peer::tRef>	mvPeerList;

		/// The node's control socket (server port).
		TcpSocket::tRef			mrControlSocket;
		
		/// The control port
		Setting<tPort>::tCacheRef mrControlPortSetting;

		public:
			PeerManager();
			~PeerManager();

			/// Sends the packet to all connected peers
			void BroadcastToPeers(const IPacket::tRef& rPacket);

			inline void AddPeer(const Peer::tRef& rPeer) {
				mvPeerList.push_back(rPeer);
			};
			inline void RemovePeer(const Peer::tRef& rPeer) {
				mvPeerList.remove(rPeer);
			};

			void OnControlSocketConnect();
			void OnControlSocketData();

			inline const TcpSocket::tRef& getControlSocket() const {
				return mrControlSocket;
			};

			// Packet processing

			static void ProcessPacket(const Packet<>::tRef& rPacket);
			static void ProcessMessage(const Packet<MessagePacket>::tRef& rPacket);

			/// Privileged commands
			static void ProcessCommand(const Packet<CommandPacket>::tRef& rPacket);
	};


	/**
	 * Opens a UDP server port, and reads packets from it.
	 **/
	class UdpListener: public UdpSocket {
		public:
			explicit UdpListener(tPort port);
			~UdpListener();

			void OnDataRecv();
	};


	/**** Packet Events *****/

	class GenericPacketEvent : public KeyedEvent<GenericPacketEvent> {
		protected:
			Packet<>::tRef mrPacket;

		public:
			GenericPacketEvent(const Packet<>::tRef& rPacket)
			: mrPacket(rPacket) {
			};

			template <class T>
			inline typename Packet<T>::tRef getPacket() const {
				return reinterpret_cast<Packet<T>*>(&*mrPacket);
			};
	};

#	define PACKET_COMPARE_MEMBER(Type, Member) \
	(int)getPacket<Type>()->getPacket()->Member - (int)pEv->getPacket<Type>()->getPacket()->Member

	class PacketEvent : public GenericPacketEvent {
	public:
		PacketEvent(const Packet<PacketData>::tRef& rPacket)
		: GenericPacketEvent(rPacket) {
		};

		EVENT(PacketEvent, GenericPacketEvent, PACKET_COMPARE_MEMBER(PacketData, PacketType));
	};

	class NetMessageEvent: public PacketEvent {
	public:
		NetMessageEvent(const Packet<MessagePacket>::tRef& rPacket)
		: PacketEvent(reinterpret_cast<Packet<> *>(&*rPacket)) {
		};

		EVENT(NetMessageEvent, PacketEvent, PACKET_COMPARE_MEMBER(MessagePacket, MessageType));
	};

	class NetPartyBroadcastEvent: public NetMessageEvent {

	};

};

#endif
